package main

import (
	"errors"
	"fmt"
	"sort"
)

// ==========切片==========

type Product struct {
	name  string
	price float64
}

// Product类型的切片
type ProductSlice []Product

func (p ProductSlice) Len() int {
	return len(p)
}

func (p ProductSlice) Less(i, j int) bool {
	return p[i].price < p[j].price // <是升序，>是降序
}

func (p ProductSlice) Swap(i, j int) {
	p[i], p[j] = p[j], p[i]
}

func (p Product) String() string {
	return fmt.Sprintf("%s (%.2f)", p.name, p.price)
}

// 将一个或多个元素插入切片A指定的位置，返回一个隐藏数组指向新地址的切片B
func InsertElement(slice []int, index int, element ...int) ([]int, error) {
	sliceLen := len(slice)
	elementLen := len(element)
	if index > sliceLen {
		return nil, errors.New(fmt.Sprintf("index %d out of slice boundary %d", index, sliceLen))
	}
	newSlice := make([]int, sliceLen+elementLen)
	endIndex := index + elementLen
	copy(newSlice, slice[:index])
	copy(newSlice[index:endIndex], element)
	copy(newSlice[endIndex:], slice[index:])
	return newSlice, nil
}

// 将一个切片插入切片A指定的位置，返回一个隐藏数组指向新地址的切片B
func InsertSlice(slice, insert []int, index int) ([]int, error) {
	sliceLen := len(slice)
	insertLen := len(insert)
	if index > sliceLen {
		return nil, errors.New(fmt.Sprintf("index %d out of slice boundary %d", index, sliceLen))
	}
	newSlice := make([]int, sliceLen+insertLen)
	endIndex := index + insertLen
	copy(newSlice, insert[:index])
	copy(newSlice[index:endIndex], insert)
	copy(newSlice[endIndex:], insert[index:])
	return newSlice, nil
}

// 删除切片索引start到end位置的元素，返回一个切片副本
func RemoveStringSliceCopy(slice []string, start, end int) []string {
	newSlice := make([]string, len(slice))
	copy(newSlice, slice)
	newSlice = append(newSlice[:start], newSlice[end:]...)
	return newSlice
}

// 截取slice的[start, end), 剩余的数据返回
func RemoveStringSliceCopyV2(slice []string, start, end int) []string {
	newSlice := make([]string, 0)
	for idx, value := range slice {
		if idx < start || idx >= end {
			newSlice = append(newSlice, value)
		}
	}
	return newSlice
}

// 删除切片中重复的项，返回新的切片
func UniqueSlice(s []int) []int {
	sMap := make(map[int]int)
	for _, value := range s {
		_, ok := sMap[value]
		if !ok {
			sMap[value] = 1
		}
	}
	var sSlice []int
	for k := range sMap {
		sSlice = append(sSlice, k)
	}
	return sSlice
}

func SliceOperator(s []int) {
	fmt.Println("SliceOperator中iSlice修改元素前")
	fmt.Printf("SliceOperator中iSlice长度=%d, 容量=%d\n", len(s), cap(s))
	fmt.Printf("SliceOperator中iSlice第一个元素=%d, 第二个元素=%d\n", s[0], s[1])
	fmt.Printf("SliceOperator中iSlice地址=%p, 第一个元素地址=%p, 第二个元素地址=%p\n", &s, &s[0], &s[1])
	fmt.Println("SliceOperator中iSlice修改元素后")
	s[0] = 1
	s[1] = 2
	fmt.Printf("SliceOperator中iSlice长度=%d, 容量=%d\n", len(s), cap(s))
	fmt.Printf("SliceOperator中iSlice第一个元素=%d, 第二个元素=%d\n", s[0], s[1])
	fmt.Printf("SliceOperator中iSlice地址=%p, 第一个元素地址=%p, 第二个元素地址=%p\n", &s, &s[0], &s[1])
	fmt.Println("SliceOperator中iSlice添加元素未超过容量时")
	s = append(s, 3)
	fmt.Printf("SliceOperator中isSlice长度=%d, 容量=%d\n", len(s), cap(s))
	fmt.Printf("SliceOperator中iSlice第一个元素=%d, 第二个元素=%d, 第三个元素=%d\n", s[0], s[1], s[2])
	fmt.Printf("SliceOperator中iSlice地址=%p, 第一个元素地址=%p, 第二个元素地址=%p, 第三个元素地址=%p\n",
		&s, &s[0], &s[1], &s[2])
	fmt.Println("SliceOperator中iSlice添加元素超过容量时")
	s = append(s, 4)
	fmt.Printf("SliceOperator中iSlice长度=%d, 容量=%d\n", len(s), cap(s))
	fmt.Printf("SliceOperator中iSlice第一个元素=%d, 第二个元素=%d, 第三个元素=%d, 第四个元素=%d\n",
		s[0], s[1], s[2], s[3])
	fmt.Printf("SliceOperator中iSlice地址=%p, 第一个元素地址=%p, 第二个元素地址=%p, "+
		"第三个元素地址=%p, 第四个元素地址=%p\n", &s, &s[0], &s[1], &s[2], &s[3])
}

func SliceOperatorPointer(s *[]int) {
	fmt.Println("SliceOperatorPointer中jSlice修改元素前")
	fmt.Printf("SliceOperatorPointer中jSlice长度=%d, 容量=%d\n", len(*s), cap(*s))
	fmt.Printf("SliceOperatorPointer中jSlice第一个元素=%d, 第二个元素=%d\n", (*s)[0], (*s)[1])
	fmt.Printf("SliceOperatorPointer中jSlice地址=%p, 第一个元素地址=%p, 第二个元素地址=%p\n", s, &(*s)[0], &(*s)[1])
	fmt.Println("SliceOperatorPointer中jSlice修改元素后")
	(*s)[0] = 1
	(*s)[1] = 2
	fmt.Printf("SliceOperatorPointer中jSlice长度=%d, 容量=%d\n", len(*s), cap(*s))
	fmt.Printf("SliceOperatorPointer中jSlice第一个元素=%d, 第二个元素=%d\n", (*s)[0], (*s)[1])
	fmt.Printf("SliceOperatorPointer中jSice地址=%p, 第一个元素地址=%p, 第二个元素地址=%p\n", s, &(*s)[0], &(*s)[1])
	fmt.Println("SliceOperatorPointer中jSlice添加元素未超过容量时")
	*s = append(*s, 3)
	fmt.Printf("SliceOperatorPointer中jSlice长度=%d, 容量=%d\n", len(*s), cap(*s))
	fmt.Printf("SliceOperatorPointer中jSlice第一个元素=%d, 第二个元素=%d, 第三个元素=%d\n", (*s)[0], (*s)[1], (*s)[2])
	fmt.Printf("SliceOperatorPointer中jSlice地址=%p, 第一个元素地址=%p, 第二个元素地址=%p, 第三个元素地址=%p\n",
		s, &(*s)[0], &(*s)[1], &(*s)[2])
	fmt.Println("SliceOperatorPointer中jSlice添加元素超过容量时")
	*s = append(*s, 4)
	fmt.Printf("SliceOperatorPointer中jSlice长度=%d, 容量=%d\n", len(*s), cap(*s))
	fmt.Printf("SliceOperatorPointer中jSlice第一个元素=%d, 第二个元素=%d, 第三个元素=%d, 第四个元素=%d\n",
		(*s)[0], (*s)[1], (*s)[2], (*s)[3])
	fmt.Printf("SliceOperatorPointer中jSlice地址=%p, 第一个元素地址=%p, 第二个元素地址=%p, "+
		"第三个元素地址=%p, 第四个元素地址=%p\n", s, &(*s)[0], &(*s)[1], &(*s)[2], &(*s)[3])
}

func main() {
	// 1、切片的定义/声明
	// 语法：var 变量名 []数据类型
	// 说明：• 定义的切片变量默认值是nil，打印结果是[]，因此无法对其访问和赋值

	// 2、切片的创建
	// 语法：• make([]Type, length, capacity)
	//		• make([]Type, length)
	//		• []Type{}
	//		• []Type{value1, value2, ..., valueN}
	// 说明：• 内置函数make()用于创建切片、映射、通道，三者都是引用类型。
	// 		• 当用于创建切片时，它会创建一个隐藏的初始化为零值的数组，并返回一个引用该隐藏数组的切片。
	//		• 切片的长度length可以通过len(slice)获取、切片的容量capacity可以通过cap(slice)获取。
	//		• 切片的容量即为其隐藏数组的长度，而其长度则为不超过该容量的任意值。
	//		• 第一种语法，切片的长度必须小于或者等于容量，第二种、第三种和第四种语法长度和容量相同。
	// 		• make([]Type, 0, 0) 等价于 make([]Type, 0) 等价于 []Type{}，都用于创建一个空切片。
	//		• 切片的赋值与索引不能超过切片的长度，slice[idx]=value与fmt.Println(slice[idx])都会引发panic。

	// 3、切片创建的底层原理
	// 底层是一个结构体，包含三部分，指针(指向创建的数组的第一个元素的地址)、切片长度、切片容量。
	/*
						   0xc00000c0e0
							-----------------------------
		make([]int, 2, 4)  | 0xc000018140|   2   |    4  |
							-----------------------------
							 | ptr        len     cap
							 |
							 V
							-------
			 0xc000018140  | 0 | 0 |
							-------
	*/

	// 4、切片的使用
	// • 引用数组数据：slice := [...]int{1, 2, 3, 4, 5}[1:4]
	// • 引用切片数据：slice := []int{1, 2, 3, 4, 5}[1:4]
	// • 直接指定数据：slice := []int{1, 2, 3, 4, 5}

	// 5、切片的索引(见原理图.png)
	// slice := make([]int, 5, 10)
	// 错误：• slice[5]			panic: runtime error: index out of range [5] with length 5
	// 		• slice[6:]			panic: runtime error: slice bounds out of range [6:5]，
	//							若右索引省略不写，则编译器会自动用切片的长度5来代替，左索引6大于右索引5，因此引发panic。
	// 		• slice[1:6:2]		invalid slice index: 6 > 2, 这里的2不同于Python的切片操作, 它不是指每隔2个取一次，
	//							它用于确定切片容量，如slice[a:b:c]，则返回的切片容量等于c-a，长度等于b-a，需满足条件：a<=b<=c，
	// 正确：• slice[5:]			等价于slice[5:5]，因此返回一个[]，长度为0，容量为5
	// 		• slice[:4]		    返回[0 0 0 0]，长度为4，容量为10, 如果左索引为0可以省略不写
	// 		• slice[3:8]		返回[0 0 0 0 0]，长度为5，容量为7
	// 		• slice[1:6:6]		返回[0 0 0 0 0]，长度为5，容量为5
	// 		• slice[6:8:9]		返回[0 0 0]，长度为2，容量为3
	// 总结：切片的容量只与切片的起始下标有关，是底层数组减去起始位置，
	// 		slice底层数组个数为10，因此对于slice[5:]和slice[3:8]，它们的容量分别是10-5=5，10-3=7，
	// 		若指定了，如slice[1:6:6]、slice[6:8:9]，则用指定的数减去左索引，即6-1=5，9-6=3，分别为它们的容量。

	// 6、切片的扩容
	// 往切片里添加元素时，当切片容量不足时，容量会扩大一倍。
	/*
		slice := make([]int, 0)
		fmt.Printf("%d, %d\n", len(slice), cap(slice))
		for i := 0; i < 5; i++ {
			slice = append(slice, -1)
			fmt.Printf("%d, %d\n", len(slice), cap(slice))
		}
		打印结果：
		0, 0
		1, 1
		2, 2
		3, 4
		4, 4
		5, 8
	*/

	// 7、切片append
	// 切片append的三种方式
	// • 添加一个或多个元素到切片
	//   var slice []int; slice = append(slice, a, b, c)
	// • 添加一个切片到切片
	//   var slice []int; slice = append(slice, []int{1, 2, 3}...)
	// • 添加一个字符串到切片(切片类型只能是[]byte)
	//   正确: var slice []byte; slice = append(slice, "ABC"...); fmt.Println(slice)  // [65 66 67]
	//   正确: var slice []byte; slice = append(slice, "你好"...); fmt.Println(slice)  // [228 189 160 229 165 189]
	//   错误: var slice []rune; slice = append(slice, "你好"...)
	//   正确: var slice []rune; slice = append(slice, []rune("你好")...); fmt.Println(slice)  // [20320 22909]
	/*
		案例1：
		s1 := make([]int, 2, 4)
		for i := range s1 {
			fmt.Printf("%p ", &s1[i])  // i代表的是切片的索引
		}
		fmt.Println()
		s1 = append(s1, 1)
		for i := range s1 {
			fmt.Printf("%p ", &s1[i])
		}
		fmt.Println()
		s1 = append(s1, 2, 3)
		for i := range s1 {
			fmt.Printf("%p ", &s1[i])
		}
		fmt.Println()
		打印结果：
		0xc00008e000 0xc00008e008
		0xc00008e000 0xc00008e008 0xc00008e010
		0xc000092000 0xc000092008 0xc000092010 0xc000092018 0xc000092020

		案例2：
		s2 := []int{1, 2}
		for i := range s2 {
			fmt.Printf("%p ", &s2[i])
		}
		fmt.Println()
		s2 = append(s2, 1)
		for i := range s2 {
			fmt.Printf("%p ", &s2[i])
		}
		fmt.Println()
		s2 = append(s2, 2)
		for i := range s2 {
			fmt.Printf("%p ", &s2[i])
		}
		fmt.Println()
		打印结果：
		0xc00001c090 0xc00001c098  // 原始s2切片底层数组各元素的地址
		0xc000018140 0xc000018148 0xc000018150  // 第一次append之后创建的新数组A中各元素的地址
		0xc000018140 0xc000018148 0xc000018150 0xc000018158  // 第二次append没有超出数组A的长度，因此再没有创建新的数组
	*/
	// 总结：对于[]int{...}这种形式创建的切片，因初始创建时其长度和容量是一样的，所以第一次append必定会创建一个新的数组，
	//		第二次进行append操作，若len(slice)+插入的元素个数 <= cap(slice)，则不会创建新的数组，如案例2所示；
	//		若len(slice)+插入的元素个数 > cap(slice)，则又会创建一个新的数组。
	//		对于make([]int, length, capacity)这种形式创建的切片，因初始创建的容量可以大于其长度，
	//		因此第一次append操作可能不会超过其容量，也就不会创建新的数组。

	// 8、切片copy
	// 示例：• 将切片B的元素拷贝到切片A，不指定索引位置
	//      - 如果切片B为空切片，则什么都不做，也不会引发panic
	//      a := make([]int, 2); b := []int{}; copy(a, b); fmt.Println(a)  // [0 0]
	//      - 如果切片B的长度L2大于切片A的长度L1，则取切片B的前L1个元素放入切片A(从切片A索引为0的位置开始放置)
	//      a := make([]int, 2); b := []int{1, 2, 3, 4}; copy(a, b); fmt.Println(a)  // [1 2]
	//      - 如果切片B的长度L2小于切片A的长度L1，则将切片B的元素全部放入切片A(从切片A索引为0的位置开始放置)
	//      a := make([]int, 2); b := []int{1}; copy(a, b); fmt.Println(a)  // [1 0]
	//      • 将切片B的元素拷贝到切片A，指定索引位置
	//      a := make([]int, 5, 8); b := []int{1, 2, 3, 4, 5}; copy(a[1:5], b[2:4]); fmt.Println(a)  // [0 3 4 0 0]
	// 说明：copy操作是拷贝的副本，因此从B切片copy到A切片的元素是2组不同的数组，它们的内存地址不一样, 对a和b分别修改元素互不影响

	// 9、切片的遍历
	// 创建一个包含指向Product指针的切片，然后立即使用3个*Product来将其初始化。
	// 之所以这么做是因为Go语言足够灵活能够识别出来一个[]*Product需要的是指向Product的指针。
	// 而基本数据类型如[]*int{1, 2, 3}则是不可以的, 必须写成: a := 1; b := 1; s := []*int{&a, &b}
	// 所以它是[]*Product{&Product{"A", 3.99}, &Product{"B", 2.49}, &Product{"C", 1.99}}的简化版。
	// 如果没有定义Product.String()方法，那么格式符号%v输出的就是Product的内存地址，而非Product的内容。
	products := []*Product{{"A", 3.99}, {"B", 2.49}, {"C", 1.99}}
	for _, p := range products {
		p.price += 0.5 // 等价于(*p).price += 0.5
	}
	fmt.Println(products) // [A (4.49) B (2.99) C (2.49)]

	// 10、切片的删除
	// • 从开始处删除s[:2]子切片
	// s := []string{"A", "B", "C", "D", "E", "F", "G"}
	// s = s[2:]
	// • 从末尾处删除s[4:]子切片
	// s := []string{"A", "B", "C", "D", "E", "F", "G"}
	// s = s[:4]
	// • 从中间删除s[1:5]
	// s := []string{"A", "B", "C", "D", "E", "F", "G"}
	// s = append(s[:1], s[5:]...)
	// 不改变原始切片的删除元素
	// s := []string{"A", "B", "C", "D", "E", "F", "G"}
	// ns := RemoveStringSliceCopy(s, 2, 4)

	// 11、切片的排序
	// 标准库中的sort提供了对整型、浮点型和字符串类型切片进行排序的函数。
	/*
		sort.Float64s(fs)				将[]float64按升序排序
		sort.Float64sAreSorted(fs)		如果[]float64是有序的则返回true
		sort.Ints(is)					将[]int按升序排序
		sort.IntsAreSorted(is)			如果[]int是有序的则返回true
		sort.Strings(ss)				按升序排序[]string类型的切片ss
		sort.StringsAreSorted(ss)		如果[]string类型的切片ss是有序的，则返回true
		sort.Sort(d)					排序类型为sort.Interface的切片d
		sort.IsSorted(d)				如果sort.Interface的值d是有序的，则返回true

		sort.Search(size, fn)			在一个排序好的数组中根据函数签名为func(int) bool的函数fn进行搜索，返回第一个使得函数fn返回值为true的索引
		sort.SearchFloat64s(fs, f)		返回有序[]float64切片fs中类型为float64的值f的索引
		sort.SearchInts(is, i)			返回有序[]int切片is中类型为int的值为i的索引
		sort.SearchStrings(ss, s)		返回有序[]string切片ss中类型为string的值为s的索引
	*/
	f64Slice := []float64{1.1, 0.1, -0.2, -1, 0.8, 0.4}
	fmt.Println(f64Slice, sort.Float64sAreSorted(f64Slice)) // [1.1 0.1 -0.2 -1 0.8 0.4] false
	sort.Float64s(f64Slice)
	fmt.Println(f64Slice, sort.Float64sAreSorted(f64Slice)) // [-1 -0.2 0.1 0.4 0.8 1.1] true

	intSlice := []int{-1, 0, 1, -2, -4, 9}
	fmt.Println(intSlice, sort.IntsAreSorted(intSlice)) // [-1 0 1 -2 -4 9] false
	sort.Ints(intSlice)
	fmt.Println(intSlice, sort.IntsAreSorted(intSlice)) // [-4 -2 -1 0 1 9] true

	stringSlice := []string{"ABC", "123", "###", "DEF", "ACD"}
	fmt.Println(stringSlice, sort.StringsAreSorted(stringSlice)) // [ABC 123 ### DEF ACD] false
	sort.Strings(stringSlice)
	fmt.Println(stringSlice, sort.StringsAreSorted(stringSlice)) // [### 123 ABC ACD DEF] true

	// 对自定义结构体类型的切片排序
	// 任何定义了Len()、Less()、Swap()方法的数据类型d都可以使用sort.Sort(d)进行排序
	productSlice := ProductSlice{{"A", 1.1}, {"B", 0.1}, {"X", -0.1}}
	fmt.Println(productSlice, sort.IsSorted(productSlice)) // [A (1.10) B (0.10) X (-0.10)] false
	sort.Sort(productSlice)
	fmt.Println(productSlice, sort.IsSorted(productSlice)) // [X (-0.10) B (0.10) A (1.10)] true

	// 对数组排序
	array := [...]int{1, 3, 43, 3, 5, 6, 32, 56, 7, 8}
	fmt.Println(array) // [1 3 43 3 5 6 32 56 7 8]
	sort.Ints(array[:])
	fmt.Println(array) // [1 3 3 5 6 7 8 32 43 56]

	// 对排序好的[]float64切片搜索
	fmt.Println(sort.SearchFloat64s(f64Slice, 0.8)) // 0.8在切片中，则返回0.8的索引4
	fmt.Println(sort.SearchFloat64s(f64Slice, 0.9)) // 0.9不在切片中，0.9在切片中应该被插入在0.8和1.1之间，所以返回5

	// 自定义搜索，找出升序排列的数组中第一个大于10的元素的位置
	i := sort.Search(len(array), func(i int) bool {
		return array[i] >= 10 // 如果array是升序则使用>=操作符，反之<=
	})
	fmt.Println(i) // 7

	// ```切片直接传入函数```
	// 1、切片变量s作为参数传给函数, 传给函数的切片变量s'的地址与s的地址不相同, 但是它们同时指向同一个底层数组
	// 因此在函数内、函数外访问切片中的元素其实都是访问同一个内存地址的数组
	// 2、基于此, 在函数内修改切片变量s'中的元素, 函数外切片变量s中对应位置的元素值也就随之改变
	// 3、如果在函数内往切片变量s'中append元素, 只要添加的元素个数不超过底层数组的容量, 就不会创建新的数组
	// 4、当在函数内往切片变量s'中append元素的个数超过了底层数组的容量, 底层会重新创建一个新的数组,
	// 切片变量s'的地址不变, 只是它指向了新的底层数组地址
	// 5、不管在函数内添加元素多少个, 在函数外, 切片变量s的长度和容量不会发生改变(前提是在函数外没有对s添加元素)
	// 所有函数外用for range遍历切片变量s, 还是只能遍历到创建切片时的长度个数, 不会受s'影响
	iSlice := make([]int, 2, 3)
	fmt.Println("main操作前")
	fmt.Printf("main中iSlice长度=%d, 容量=%d\n", len(iSlice), cap(iSlice))
	fmt.Printf("main中iSlice第一个元素=%d, 第二个元素=%d\n", iSlice[0], iSlice[1])
	fmt.Printf("main中iSlice地址=%p, 第一个元素地址=%p, 第二个元素地址=%p\n", &iSlice, &iSlice[0], &iSlice[1])
	SliceOperator(iSlice)
	fmt.Println("main操作后")
	fmt.Printf("main中iSlice长度=%d, 容量=%d\n", len(iSlice), cap(iSlice))
	fmt.Printf("main中iSlice第一个元素=%d, 第二个元素=%d\n", iSlice[0], iSlice[1])
	fmt.Printf("main中iSlice地址=%p, 第一个元素地址=%p, 第二个元素地址=%p\n", &iSlice, &iSlice[0], &iSlice[1])
	/*
		main操作前
		main中isSlice长度=2, 容量=3
		main中isSlice第一个元素=0, 第二个元素=0
		main中isSlice地址=0xc00000c480, 第一个元素地址=0xc000018140, 第二个元素地址=0xc000018148
		SliceOperator中iSlice修改元素前
		SliceOperator中iSlice长度=2, 容量=3
		SliceOperator中iSlice第一个元素=0, 第二个元素=0
		SliceOperator中iSlice地址=0xc00000c4a0, 第一个元素地址=0xc000018140, 第二个元素地址=0xc000018148
		SliceOperator中iSlice修改元素后
		SliceOperator中iSlice长度=2, 容量=3
		SliceOperator中iSlice第一个元素=1, 第二个元素=2
		SliceOperator中iSlice地址=0xc00000c4a0, 第一个元素地址=0xc000018140, 第二个元素地址=0xc000018148
		SliceOperator中iSlice添加元素未超过容量时
		SliceOperator中iSlice长度=3, 容量=3
		SliceOperator中iSlice第一个元素=1, 第二个元素=2, 第三个元素=3
		SliceOperator中iSlice地址=0xc00000c4a0, 第一个元素地址=0xc000018140, 第二个元素地址=0xc000018148, 第三个元素地址=0xc000018150
		SliceOperator中iSlice添加元素未超过容量时
		SliceOperator中iSlice长度=4, 容量=6
		SliceOperator中iSlice第一个元素=1, 第二个元素=2, 第三个元素=3, 第四个元素=4
		SliceOperator中iSlice地址=0xc00000c4a0, 第一个元素地址=0xc00001a1b0, 第二个元素地址=0xc00001a1b8, 第三个元素地址=0xc00001a1c0, 第四个元素地址=0xc00001a1c8
		main操作后
		main中iSlice长度=2, 容量=3
		main中iSlice第一个元素=1, 第二个元素=2
		main中iSlice地址=0xc00000c480, 第一个元素地址=0xc000018140, 第二个元素地址=0xc000018148
	*/

	// ```切片地址传入函数```
	// 1、切片变量s的地址作为参数传给函数, 传给函数的切片变量s'的值就等于s的地址,
	// 在函数内用s'修改或者添加元素, 都相当于直接对函数外的切片操作
	// 2、同样的, 当添加的元素个数超过切片指向的底层数组容量时, 就会创建一个新的数组, 切片指向新数组
	jSlice := make([]int, 2, 3)
	fmt.Println("main操作前")
	fmt.Printf("main中jSlice长度=%d, 容量=%d\n", len(jSlice), cap(jSlice))
	fmt.Printf("main中jSlice第一个元素=%d, 第二个元素=%d\n", jSlice[0], jSlice[1])
	fmt.Printf("main中jSlice地址=%p, 第一个元素地址=%p, 第二个元素地址=%p\n", &jSlice, &jSlice[0], &jSlice[1])
	SliceOperatorPointer(&jSlice)
	fmt.Println("main操作后")
	fmt.Printf("main中jSlice长度=%d, 容量=%d\n", len(jSlice), cap(jSlice))
	fmt.Printf("main中jSlice第一个元素=%d, 第二个元素=%d\n", jSlice[0], jSlice[1])
	fmt.Printf("main中jSlice地址=%p, 第一个元素地址=%p, 第二个元素地址=%p\n", &jSlice, &jSlice[0], &jSlice[1])
	/*
		main操作前
		main中jSlice长度=2, 容量=3
		main中jSlice第一个元素=0, 第二个元素=0
		main中jSlice地址=0xc0000a6460, 第一个元素地址=0xc0000c2060, 第二个元素地址=0xc0000c2068
		SliceOperatorPointer中jSlice修改元素前
		SliceOperatorPointer中jSlice长度=2, 容量=3
		SliceOperatorPointer中jSlice第一个元素=0, 第二个元素=0
		SliceOperatorPointer中jSlice地址=0xc0000a6460, 第一个元素地址=0xc0000c2060, 第二个元素地址=0xc0000c2068
		SliceOperatorPointer中jSlice修改元素后
		SliceOperatorPointer中jSlice长度=2, 容量=3
		SliceOperatorPointer中jSlice第一个元素=1, 第二个元素=2
		SliceOperatorPointer中jSlice地址=0xc0000a6460, 第一个元素地址=0xc0000c2060, 第二个元素地址=0xc0000c2068
		SliceOperatorPointer中jSlice添加元素未超过容量时
		SliceOperatorPointer中jSlice长度=3, 容量=3
		SliceOperatorPointer中jSlice第一个元素=1, 第二个元素=2, 第三个元素=3
		SliceOperatorPointer中jSlice地址=0xc0000a6460, 第一个元素地址=0xc0000c2060, 第二个元素地址=0xc0000c2068, 第三个元素地址=0xc0000c2070
		SliceOperatorPointer中jSlice添加元素超过容量时
		SliceOperatorPointer中jSlice长度=4, 容量=6
		SliceOperatorPointer中jSlice第一个元素=1, 第二个元素=2, 第三个元素=3, 第四个元素=4
		SliceOperatorPointer中jSlice地址=0xc0000a6460, 第一个元素地址=0xc0000b60c0, 第二个元素地址=0xc0000b60c8, 第三个元素地址=0xc0000b60d0, 第四个元素地址=0xc0000b60d8
		main操作后
		main中jSlice长度=4, 容量=6
		main中jSlice第一个元素=1, 第二个元素=2
		main中jSlice地址=0xc0000a6460, 第一个元素地址=0xc0000b60c0, 第二个元素地址=0xc0000b60c8
	*/
}
